/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.lightsh.scroller;

import android.content.Context;
import android.hardware.SensorManager;
import android.util.FloatMath;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;

/**
 * 该类负责控件(可进行scroll的控件)滚动时的各个状态和位置的计算
 * 支持越界的滚动和回弹
 */
public class LOverScroller {
	private final static String TAG = "LOverScroller";
	
	/**
	 * 记录当前所处的滚动状态，可能取值为:
	 * <br/>{@link #SCROLL_MODE}
	 * <br/>{@link #FLINGL_MODE}
	 */
    private int mMode;

    // 分别对x和y方向进行滚动计算
    private MagneticOverScroller mScrollerX;
    private MagneticOverScroller mScrollerY;

    /**
     *  scroll滚动时使用的插值器,如果是null时，使用默认的计算方式{@link #viscousFluid(float)}
     */
    private Interpolator mInterpolator;

    private static final int DEFAULT_DURATION = 250;
    /**
     * scroll模式
     */
    private static final int SCROLL_MODE = 0;
    /**
     * fling模式
     */
    private static final int FLING_MODE = 1;
    
    
    private static float sViscousFluidScale;
    private static float sViscousFluidNormalize;
    static {
        // This controls the viscous fluid effect (how much of it)
        sViscousFluidScale = 8.0f;
        // must be set to 1.0 (used in viscousFluid())
        sViscousFluidNormalize = 1.0f;
        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
    }    
    /**
     *  根据一个特殊算法，得到滚动的轨迹比例。
     *  应该是类似流动滚动，停止前减速？
     */
    static float viscousFluid(float x)
    {
        x *= sViscousFluidScale;
        if (x < 1.0f) {
            x -= (1.0f - (float)Math.exp(-x));
        } else {
            float start = 0.36787944117f;   // 1/e == exp(-1)
            x = 1.0f - (float)Math.exp(1.0f - x);
            x = start + x * (1.0f - start);
        }
        x *= sViscousFluidNormalize;
        return x;
    }
    
    /**
     * 创建一个Scroller，不指定interpolator
     */
    public LOverScroller(Context context) {
        this(context, null);
    }

    /**
     * 创建一个指定了interpolator的Scroller，当scrollTo()时，计算移动比例时使用指定的interpolator
     */
    public LOverScroller(Context context, Interpolator interpolator) {
        this(context, interpolator, MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT,
                MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT);
    }

    /**
     * Creates an OverScroller.
     * @param context The context of this application.
     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
     * be used.
     * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
     * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
     * means no bounce.
     * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction.
     */
    public LOverScroller(Context context, Interpolator interpolator,
            float bounceCoefficientX, float bounceCoefficientY) {
        mInterpolator = interpolator;
        mScrollerX = new MagneticOverScroller();
        mScrollerY = new MagneticOverScroller();
        MagneticOverScroller.initializeFromContext(context);

        mScrollerX.setBounceCoefficient(bounceCoefficientX);
        mScrollerY.setBounceCoefficient(bounceCoefficientY);
    }

    /**
     * 返回是否已经结束了scrolling
     */
    public final boolean isFinished() {
        return mScrollerX.mFinished && mScrollerY.mFinished;
    }

    /**
     * 强制停止scrolling，但不将scroll的位置设置到最终的x、y位置。
     * 与{@link #abortAnimation()}相反。
     */
    public final void forceFinished(boolean finished) {
        mScrollerX.mFinished = mScrollerY.mFinished = finished;
    }

    /**
     * 返回当前的x位置
     */
    public final int getCurrX() {
        return mScrollerX.mCurrentPosition;
    }

    /**
     * 返回当前的x位置
     */
    public final int getCurrY() {
        return mScrollerY.mCurrentPosition;
    }

    /**
     * @hide
     * 返回当前的速度，该值的xy方向速度的合成值
     */
    public float getCurrVelocity() {
        float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
        squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
        return FloatMath.sqrt(squaredNorm);
    }

    /**
     * 返回在scroll时起始的x位置。
     */
    public final int getStartX() {
        return mScrollerX.mStart;
    }

    /**
     * 返回在scroll时起始的y位置。
     */
    public final int getStartY() {
        return mScrollerY.mStart;
    }

    /**
     * 返回scroll的结束的x值. 
     * 只对 "fling" scrolls 状态有效.因为"scroll"时是根据duration结束的.
     */
    public final int getFinalX() {
        return mScrollerX.mFinal;
    }

    /**
     * 返回scroll的结束的y值. 
     * 只对 "fling" scrolls 状态有效.因为"scroll"时是根据duration结束的.
     */
    public final int getFinalY() {
        return mScrollerY.mFinal;
    }

    /**
     * Returns how long the scroll event will take, in milliseconds.
     *
     * @return The duration of the scroll in milliseconds.
     *
     * @hide Pending removal once nothing depends on it
     * @deprecated OverScrollers don't necessarily have a fixed duration.
     *             This function will lie to the best of its ability.
     */
    public final int getDuration() {
        return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
    }

    /**
     * Extend the scroll animation. This allows a running animation to scroll
     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
     *
     * @param extend Additional time to scroll in milliseconds.
     * @see #setFinalX(int)
     * @see #setFinalY(int)
     *
     * @hide Pending removal once nothing depends on it
     * @deprecated OverScrollers don't necessarily have a fixed duration.
     *             Instead of setting a new final position and extending
     *             the duration of an existing scroll, use startScroll
     *             to begin a new animation.
     */
    public void extendDuration(int extend) {
        mScrollerX.extendDuration(extend);
        mScrollerY.extendDuration(extend);
    }

    /**
     * Sets the final position (X) for this scroller.
     *
     * @param newX The new X offset as an absolute distance from the origin.
     * @see #extendDuration(int)
     * @see #setFinalY(int)
     *
     * @hide Pending removal once nothing depends on it
     * @deprecated OverScroller's final position may change during an animation.
     *             Instead of setting a new final position and extending
     *             the duration of an existing scroll, use startScroll
     *             to begin a new animation.
     */
    public void setFinalX(int newX) {
        mScrollerX.setFinalPosition(newX);
    }

    /**
     * Sets the final position (Y) for this scroller.
     *
     * @param newY The new Y offset as an absolute distance from the origin.
     * @see #extendDuration(int)
     * @see #setFinalX(int)
     *
     * @hide Pending removal once nothing depends on it
     * @deprecated OverScroller's final position may change during an animation.
     *             Instead of setting a new final position and extending
     *             the duration of an existing scroll, use startScroll
     *             to begin a new animation.
     */
    public void setFinalY(int newY) {
        mScrollerY.setFinalPosition(newY);
    }

    /**
     * 当你需要知道当前的位置时，请调用该方法.
     * <br/>返回true表名scroll还没结束
     */
    public boolean computeScrollOffset() {
        if (isFinished()) {
        	// 只有当两个scroller状态都结束时才结束。
        	// 除了这里之外，下面的流程总会返回true。
            return false;
        }

        switch (mMode) {
            case SCROLL_MODE:
            	// scroll模式下，直接计算出移动的比例
                long time = AnimationUtils.currentAnimationTimeMillis();
                // 使用x或者y的scroller是一样的，这里使用x的
                final long elapsedTime = time - mScrollerX.mStartTime;
                final int duration = mScrollerX.mDuration;
                if (elapsedTime < duration) {
                    float q = (float) (elapsedTime) / duration;
                    // 优先使用设置好的插值器
                    if (mInterpolator == null)
                        q = viscousFluid(q);
                    else
                        q = mInterpolator.getInterpolation(q);

                    mScrollerX.updateScroll(q);
                    mScrollerY.updateScroll(q);
                } else {
                	// 当时间超出duration时，结束
                    abortAnimation();
                }
                break;
            case FLING_MODE:
            	// 分别对x/y的scroller进行计算
                if (!mScrollerX.mFinished) {
                	// 更新位置和时间
                    if (!mScrollerX.update()) {
                    	// update返回false表明当前滚动结束,需要判断是否需要进行下一个阶段的滚动
                        if (!mScrollerX.continueWhenFinished()) {
                        	// 真正的fling结束
                            mScrollerX.finish();
                        }
                    }
                }

                if (!mScrollerY.mFinished) {
                    if (!mScrollerY.update()) {
                        if (!mScrollerY.continueWhenFinished()) {
                            mScrollerY.finish();
                        }
                    }
                }

                break;
        }

        return true;
    }

    /**
     * 根据给定的开始位置和需要滚动的距离进行滚动动作。
     * 滚动持续时间使用默认的{@link #DEFAULT_DURATION}
     * Start scrolling by providing a starting point and the distance to travel.
     * The scroll will use the default value of 250 milliseconds for the
     * duration.
     *
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     */
    public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }

    /**
     * 根据给定的开始位置、需要滚动的距离以及持续时间进行滚动动作。
     *
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     * @param duration Duration of the scroll in milliseconds.
     */
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mScrollerX.startScroll(startX, dx, duration);
        mScrollerY.startScroll(startY, dy, duration);
    }

    /**
     * 当需要进行spring back动画时，调用该方法。
     *
     * @param startX 开始的x坐标
     * @param startY 开始的y坐标
     * @param minX spring back过程中x坐标的左边界值(即为控件的最左边坐标)
     * @param maxX spring back过程中x坐标的右边界值
     * @param minY spring back过程中x坐标的上边界值
     * @param maxY spring back过程中x坐标的下边界值
     * @return 如果spring back初始化成功返回true;如果当前的位置处于合法的位置，
     * 			即startX和startY都在边界范围内，返回false
     */
    public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
        mMode = FLING_MODE;

        // Make sure both methods are called.
        final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
        final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
        return spingbackX || spingbackY;
    }
    
    /**
     * 使用该方法来实现滚动停止时的震动效果,从开始位置shake回目标位置
     * (该方法与{@link #springBack(int, int, int, int, int, int)} 类似，
     * 只是回弹的速度有点不一致)
     * @param startX			震动开始的x位置，该位置与目标位置有小小的偏移
     * @param startY			震动开始的y位置，该位置与目标位置有小小的偏移
     * @param desX				目标位置x
     * @param desY				目标位置y
     * @return					如果动画初始化成功返回true，如果不需要进行动画返回false
     */
    public boolean springBackForShake(int startX, int startY, int desX, int desY){
    	mMode = FLING_MODE;

        // Make sure both methods are called.
        final boolean spingbackX = mScrollerX.springbackForShake(startX, desX);
        final boolean spingbackY = mScrollerY.springbackForShake(startY, desY);
        return spingbackX || spingbackY;
    }
    

    /**
     * 根据给定的初始速度进行fling动画。详情查看{@link #fling(int, int, int, int, int, int, int, int, int, int)}
     * <br/>overX 和 overY 使用默认值0
     */
    public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY) {
        fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
    }

    /**
     * 根据给定的初始速度进行fling动画。
     *
     * @param startX Starting point of the scroll (X)
     * @param startY Starting point of the scroll (Y)
     * @param velocityX Initial velocity of the fling (X) measured in pixels per
     *            second.
     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
     *            second
     * @param minX Minimum X value. The scroller will not scroll past this point
     *            unless overX > 0. If overfling is allowed, it will use minX as
     *            a springback boundary.
     * @param maxX Maximum X value. The scroller will not scroll past this point
     *            unless overX > 0. If overfling is allowed, it will use maxX as
     *            a springback boundary.
     * @param minY Minimum Y value. The scroller will not scroll past this point
     *            unless overY > 0. If overfling is allowed, it will use minY as
     *            a springback boundary.
     * @param maxY Maximum Y value. The scroller will not scroll past this point
     *            unless overY > 0. If overfling is allowed, it will use maxY as
     *            a springback boundary.
     * @param overX Overfling range. If > 0, horizontal overfling in either
     *            direction will be possible.
     * @param overY Overfling range. If > 0, vertical overfling in either
     *            direction will be possible.
     */
    public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY, int overX, int overY) {
        mMode = FLING_MODE;
        mScrollerX.fling(startX, velocityX, minX, maxX, overX);
        mScrollerY.fling(startY, velocityY, minY, maxY, overY);
    }

    /**
     * Notify the scroller that we've reached a horizontal boundary.
     * Normally the information to handle this will already be known
     * when the animation is started, such as in a call to one of the
     * fling functions. However there are cases where this cannot be known
     * in advance. This function will transition the current motion and
     * animate from startX to finalX as appropriate.
     *
     * @param startX Starting/current X position
     * @param finalX Desired final X position
     * @param overX Magnitude of overscroll allowed. This should be the maximum
     *              desired distance from finalX. Absolute value - must be positive.
     */
    public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
    	mMode = FLING_MODE;
        mScrollerX.notifyEdgeReached(startX, finalX, overX);
    }

    /**
     * Notify the scroller that we've reached a vertical boundary.
     * Normally the information to handle this will already be known
     * when the animation is started, such as in a call to one of the
     * fling functions. However there are cases where this cannot be known
     * in advance. This function will animate a parabolic motion from
     * startY to finalY.
     *
     * @param startY Starting/current Y position
     * @param finalY Desired final Y position
     * @param overY Magnitude of overscroll allowed. This should be the maximum
     *              desired distance from finalY.
     */
    public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
    	mMode = FLING_MODE;
        mScrollerY.notifyEdgeReached(startY, finalY, overY);
    }

    /**
     * 返回当前的fling状态是否超出边缘，即是否正在over scroll。
     * <br/>边缘由{@link #fling(int, int, int, int, int, int, int, int, int, int)}给出。
     *
     * One should check this value before calling
     * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
     * to restore a valid position will then be stopped. The caller has to take into account
     * the fact that the started scroll will start from an overscrolled position.
     *
     * @return true when the current position is overscrolled and in the process of
     *         interpolating back to a valid value.
     */
    public boolean isOverScrolled() {
        return ((!mScrollerX.mFinished &&
                mScrollerX.mState != MagneticOverScroller.FLING) ||
                (!mScrollerY.mFinished &&
                        mScrollerY.mState != MagneticOverScroller.FLING));
    }

    /**
     * 结束滚动动画，并且使x、y坐标到达动画的最终位置。
     * <br/>该方法与{@link #forceFinished(boolean)}相反。
     *
     * @see #forceFinished(boolean)
     */
    public void abortAnimation() {
        mScrollerX.finish();
        mScrollerY.finish();
    }

    /**
     * 获取经过的时间。这里以x/y中开始时间较早的为准，即取经过时间的较大值     *
     * @hide
     */
    public int timePassed() {
        final long time = AnimationUtils.currentAnimationTimeMillis();
        final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
        return (int) (time - startTime);
    }
    
    /**
     * fling时，是否使用重力减速方式。
     * 如果设置为false，则使用移动距离线性减少的方式？
     * @hide
     */
    public void setEnabledGravityDeceleration(boolean enabled){
        mScrollerX.setEnabledGravityDeceleration(enabled);
        mScrollerY.setEnabledGravityDeceleration(enabled);
    }

    /**
     * 一个方向上的scroll计算类
     */
    static class MagneticOverScroller {
    	/**
    	 * 初始位置
    	 */
        int mStart;

        /**
         * 当前位置
         */
        int mCurrentPosition;

        /**
         * 最终位置(动画结束时的位置)
         */
        int mFinal;

        /**
         * 初始速度
         */
        int mVelocity;

        /**
         * 当前速度
         */
        float mCurrVelocity;

        /**
         * 当前的加速度
         */
        float mDeceleration;

        /**
         * 动画开始的时间
         */
        long mStartTime;

        /**
         * 动画完成需要的时间
         */
        int mDuration;

        /**
         * 动画是否已经结束
         */
        boolean mFinished;

        /**
         *  重力常量, 减速时使用
         */
        static float GRAVITY;

        private int mIterateCount = 0;
        private float mLastDistance = 0.0f;
        private float mDelta = 0.0f;
        private float mCoeffDeceleration = 0.0f;
        private int mSpringbackEnd = 0;
        private int mSpringDistance = 0;
        
        /**
         * 是否使用重力减速方式
         */
        private boolean mGravityDeceleration = false;
        
        // FIXME 测试用
        private static final int MAXFLINGTESTCOUNT = 4;
        private final static int MAXUPDATECOUNT = 5;
        private int mFlingTestCount = 1;
        private int mUpdateCount = 0;
        
        private static final int DECELERATIONSPEED_FAST = 50;
        private static final int DECELERATIONSPEED_SLOW = 25;
        private int mDecelerationSpeed = DECELERATIONSPEED_FAST;
        private long mAverageTime = 0;
        
        /**
         * 初始化重力常量
         */
        static void initializeFromContext(Context context) {
            final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
            GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2)
                    * 39.37f // inch/meter
                    * ppi // pixels per inch
                    * ViewConfiguration.getScrollFriction();
        }

        /*
         * view的fling过程可以分为三个阶段,(以bottom over scroll为例)
         * 
         * view的尺寸边距
         * |----------------------------------| view的bottom
         * 
         * 1.FLING:view正常滚动，其最后一个子view的bottom在它之下
         * 
         * 子views
         * |--------------------------------------------|
         * 									  |		<----向着parent的bottom移动
         * 
         * 2.OVERFLING:最后一个子view的bottom在parent的bottom之上了，但仍要远离parent的bottom移动
         * 
         * 子views
         * |-------------------------- |
         *  远离parent的bottom移动 <-----		  |	
         *  
         * 3.SPRINGBACK:越界后，最终要归位。
         * 
         * 子views
         * |-------------------------- |
         * 		  向着parent的bottom移动 -----> |	
         */

        // fling分三个过程
        private static final int FLING = 0;
        private static final int OVERFLING = 1;
        private static final int SPRINGBACK = 2;
        
        // 这个状态是为了实现快速回弹
        private static final int SPRINGBACK_FOR_SHAKE = 3;
        
        /**
         *  当前所处的scroll状态
         */
        private int mState = FLING;

        /**
         * 允许的over scroll距离
         */
        private int mOver;

        /**
         * 回弹的时间，
         */
        private static final int OVERSCROLL_SPRINGBACK_DURATION = 600;

        // Oscillation period
        /**
         * 震荡周期?
         */
        private static final float TIME_COEF =
            1000.0f * (float) Math.PI / OVERSCROLL_SPRINGBACK_DURATION;

        // If the velocity is smaller than this value, no bounce is triggered
        // when the edge limits are reached (would result in a zero pixels
        // displacement anyway).
        /**
         * 允许回弹的最低速度，若少于这个速度，则不会有回弹效果？
         */
        private static final float MINIMUM_VELOCITY_FOR_BOUNCE = Float.MAX_VALUE;//140.0f;

        /**
         * 当fling到view的边缘时，会根据当前的速度乘以这个比例作为回弹的初速度。
         */
        private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f;

        /**
         * 为什么没有使用？
         */
        private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;

        MagneticOverScroller() {
            mFinished = true;
            mFlingTestCount = 1;
            mAverageTime = 0;
        }

        /**
         * 根据滚动的时间比例值q，计算当前位置mCurrentPosition
         */
        void updateScroll(float q) {
            mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
        }

        /**
         * Get a signed deceleration that will reduce the velocity.
         * <br/>根据速度获取对应的减速度(带方向的,与速度相反)
         */
        static float getDeceleration(int velocity) {
            return velocity > 0 ? -GRAVITY : GRAVITY;
        }

        /**
         * 返回由start 到 end 两点所需要的滚动时间(start到end间不会碰撞,初始速度和减速度已经给出);如果到不了，则返回0
         * <br/>因为设置了end位置，所以该方法一般是返回碰撞之前的时间点
         */
        static int computeDuration(int start, int end, float initialVelocity, float deceleration) {
        	// 滚动距离
        	final int distance = start - end;
        	// 计算出需要运行的时间系数值
            final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration
                    * distance;
            if (discriminant >= 0.0f) {
                float delta = (float) Math.sqrt(discriminant);
                if (deceleration < 0.0f) {
                    delta = -delta;
                }
                return (int) (1000.0f * (-initialVelocity - delta) / deceleration);
            }

            // 到不了end位置
            return 0;
        }

        /**
         * 开始滚动，需要初始化开始位置/结束位置/开始时间/总时间等变量
         */
        void startScroll(int start, int distance, int duration) {
            mFinished = false;

            mStart = start;
            mFinal = start + distance;

            mStartTime = AnimationUtils.currentAnimationTimeMillis();
            mDuration = duration;

            // Unused
            mDeceleration = 0.0f;
            mVelocity = 0;
        }

        /**
         * 结束动画，并将当前位置设置为最终位置
         */
        void finish() {
            mCurrentPosition = mFinal;
            // Not reset since WebView relies on this value for fast fling.
            // mCurrVelocity = 0.0f;
            mFinished = true;
        }

        void setFinalPosition(int position) {
            mFinal = position;
            mFinished = false;
        }

        /**
         *  这个方法并非将时间延长extend,而是修改持续时间，将持续时间设置为“现在时间再加上extend“
         */
        void extendDuration(int extend) {
            final long time = AnimationUtils.currentAnimationTimeMillis();
            final int elapsedTime = (int) (time - mStartTime);
            mDuration = elapsedTime + extend;
            mFinished = false;
        }

        void setBounceCoefficient(float coefficient) {
            mBounceCoefficient = coefficient;
        }

        /**
         * 回弹，注意这里默认是回弹到min或者max其中一端.使用默认的持续时间
         * @param start
         * @param min
         * @param max
         * @return 如果滚动结束，返回false（！mFinished值）
         */
        boolean springback(int start, int min, int max) {
            mFinished = true;

            mStart = start;

            mStartTime = AnimationUtils.currentAnimationTimeMillis();
            mDuration = 0;

            if (start < min) {
                startSpringback(start, min, false);
            } else if (start > max) {
                startSpringback(start, max, true);
            }
        	
            return !mFinished;
        }
        
        private void startSpringback(int start, int end, boolean positive) {
            mFinished = false;
            mState = SPRINGBACK;
            mCoeffDeceleration = 0.7f;
            mFinal = end;
            int distance = end - start;
            mDelta = (float) (distance * (1 - 0.95f) / (1 - Math.pow(0.95, 50)));
            mVelocity = (int) (mDelta * mDecelerationSpeed);
        	mCurrVelocity = mVelocity;
        	mLastDistance = 0.0f;
        	
        	mSpringDistance = distance;
        	
        	if(mSpringDistance == 0)
        		mDuration = 0;
        	else
        		mDuration = 1000;
        	mStartTime = AnimationUtils.currentAnimationTimeMillis();
        }
        
        /**
         * 为了shake，快速回弹
         * @param start
         * @param end
         * @param positive
         */
        private void startSpringbackForShake(int start, int end, boolean positive) {
            mFinished = false;
            mState = SPRINGBACK_FOR_SHAKE;
            mCoeffDeceleration = 0.7f;
            mFinal = end;
            int distance = end - start;
            mDelta = (float) (distance * (1 - 0.95f) / (1 - Math.pow(0.95, 50)));
            mVelocity = (int) (mDelta * mDecelerationSpeed);
        	mCurrVelocity = mVelocity;
        	mLastDistance = 0.0f;
        	
        	mSpringDistance = distance;
        	
        	if(mSpringDistance == 0)
        		mDuration = 0;
        	else
        		// 这里是晃动的时间.其实这个时最大时间，而不是真实时间，真实时间由end确定
        		mDuration = 300;
        	mStartTime = AnimationUtils.currentAnimationTimeMillis();
        }
        
        /**
         * 为了shake，快速回弹
         */
        boolean springbackForShake(int start, int des){
        	mFinished = true;

            mStart = start;

            mStartTime = AnimationUtils.currentAnimationTimeMillis();
            mDuration = 0;
            
            if(start < des){
            	startSpringbackForShake(start, des, false);
            }else if(start > des){
            	startSpringbackForShake(start, des, true);
            }
        	
            return !mFinished;
        }
        
        /**
         * 正常的fing
         * 
         * @param start 初始位置
         * @param velocity 初始速度
         * @param min 最小位置
         * @param max 最大位置
         * @param over 允许over scroll的范围
         */
        void fling(int start, int velocity, int min, int max, int over) {
            mState = FLING;
            mOver = over;

            mFinished = false;
            mStart = start;
            mCurrentPosition = start;           
            mStartTime = AnimationUtils.currentAnimationTimeMillis();
            mVelocity = velocity;
            mCurrVelocity = mVelocity;
            int absVelocity = Math.abs(mVelocity);
            mFlingTestCount++;
            mUpdateCount = 0;
            // 初始化fling, 包括:总的运动时间，最终位置
            
            if(mGravityDeceleration){
                // 使用匀减速方式，距离根据时间确定
                mDeceleration = getDeceleration(velocity);
                // Duration are expressed in milliseconds
                mDuration = (int) (-1000.0f * velocity / mDeceleration);
                mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
            } else {
                // 每一次的距离根据比例减少
                mIterateCount = 0;
                mLastDistance = 0.0f;
                mCoeffDeceleration = 0.97f;
                
                int delta = 0;
                int i = 0;
                
                mDelta = 1.0f*mVelocity/mDecelerationSpeed;
                
                while(true) {
                    delta = (int)(mDelta * Math.pow(mCoeffDeceleration, i));
                    if(delta == 0) {
                        mIterateCount = i;
                        break;
                    }
                    i++;
                }
                int totalDistance = (int) (mDelta * (1.0f - Math.pow(mCoeffDeceleration, i)) / (1.0f - mCoeffDeceleration));
                
                // 这里根据速度设置持续时间？会导致持续时间时固定只能有3个值？
                if(absVelocity > 2000)
                    mDuration = 5000;
                else if(absVelocity < 200)
                    mDuration = 0;
                else
                    mDuration = 3000;
                
                mFinal = mStart + totalDistance;
            }
            
            // FIXME 不需要重新计算总时间了,上面的的持续时间只是3个值?
            if (mFinal < min) {
                mFinal = min;
            }

            if (mFinal > max) {
                mFinal = max;
            }
            
            // 初始时，位置已经越界
            // 如果未达到over fling 界限，继续over fling
            // 否则，直接执行spring back
            if (start > max) {
                if (start >= max + over) {
                	// 超出了over的范围，需要回弹，修正开始位置从最大值开始
                    springback(max + over, min, max);
                } else {
                    if (velocity <= 0) {
                    	// 速度与回弹方向相同，回弹
                        springback(start, min, max);
                    } else {
                    	// 速度与回弹方向相反，需要进行碰撞判断
                        long time = AnimationUtils.currentAnimationTimeMillis();
                        //模拟从view 的edge 运动到此距离的时间
                        final double durationSinceEdge =
                            Math.atan((start-max) * TIME_COEF / velocity) / TIME_COEF;
                        mStartTime = (int) (time - 1000.0f * durationSinceEdge);

                        // Simulate a bounce that started from edge
                        
                        /*
                         * 					max over scroll distance
                         *  |----------------|--------| list bottom
                         *  -----------------|---| last child bottom 
                         *  			      <---over scroll(Action Up),begin to over fling
                         *  
                         *  在这种情况下，已经处于over scroll但未到达max over scroll distance,此时，仍要执行一段fling
                         *  这里是模拟从list bottom 的位置开始执行回弹的，实际上是离list bottom的一段距离开始的，所以，要
                         *  算出这段距离在回弹的过程中的
                         */
                        mStart = max;

                        mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));

                        onEdgeReached();
                    }
                }
            } else {
                if (start < min) {
                    if (start <= min - over) {
                        springback(min - over, min, max);
                    } else {
                        if (velocity >= 0) {
                            springback(start, min, max);
                        } else {
                            long time = AnimationUtils.currentAnimationTimeMillis();
                            final double durationSinceEdge =
                                Math.atan((start-min) * TIME_COEF / velocity) / TIME_COEF;
                            mStartTime = (int) (time - 1000.0f * durationSinceEdge);

                            // Simulate a bounce that started from edge
                            mStart = min;

                            mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));

                            onEdgeReached();
                        }

                    }
                }
            }
        }

        //viewfling到了边缘，通过此方法来通知scroller
        /**
         * viewfling到了边缘，通过此方法来通知scroller
         * @param start 起始位置
         * @param end 结束位置
         * @param over 允许越界范围
         */
        void notifyEdgeReached(int start, int end, int over) {
        	if(Math.abs(start) < over && (start != end)) {
        		mState = OVERFLING;
            	mCoeffDeceleration = 0.5f;
            	mLastDistance = 0;
            	mStart = start;
            	mSpringbackEnd = end;
            	mFinished = false;
            	mDuration = Integer.MAX_VALUE;
            	
            	int delta = 0;
                int i = 0;
                
                mDelta = mCurrVelocity / 150.0f;
                while(true) {
                    delta = (int)(mDelta * Math.pow(mCoeffDeceleration, i));
                    if(delta == 0) {
                    	mIterateCount = i;
                        break;
                    }
                    i++;
                }
                int totalDistance = (int) (mDelta * (1.0f - Math.pow(mCoeffDeceleration, i)) / (1.0f - mCoeffDeceleration));
                mFinal = mStart + totalDistance;
        	} else {
        		springback(start, 0, end);
        	}
        	
        }

        /**
         * 当碰撞到边缘时，更改运动方式
         * <br/>修改mState,mFinal和mDuration的值,实现第二阶段的减速前冲和第三阶段的回弹
         * 注意，在调用该方法前，需要初始化mStart,mStartTiem, mVelocity, mDeceleration，mOver值
         */
        private void onEdgeReached() {
        	// 越界距离只是简单的以初速度乘以一个比例
            final float distance = mVelocity / TIME_COEF;
            // FIXME 这里的状态转换有问题？
            if (Math.abs(distance) < mOver) {
                // Spring force will bring us back to final position
                mState = FLING;
                mFinal = mStart;
                mDuration = OVERSCROLL_SPRINGBACK_DURATION;
            } else {
                // Velocity is too high, we will hit the boundary limit
                mState = FLING;
                int over = mVelocity > 0 ? mOver : -mOver;
                mFinal = mStart + over;
//                mDuration = (int) (1000.0f * Math.asin(over / distance) / TIME_COEF);
                mDuration = OVERSCROLL_SPRINGBACK_DURATION;
            }
        }

        /**
         * 当scroll的经过的时间比mDuration要长时,当前状态结束，需要进入下一个状态。
         * <br/>进行这个碰撞判断
         * <br/>当滚动真正结束时，返回false
         */
        boolean continueWhenFinished() { 
        	// FIXME 
            switch (mState) {
                case FLING:
                	return false;
                case SPRINGBACK_FOR_SHAKE:
                case SPRINGBACK:
                    return false;
                case OVERFLING:
                	mState = SPRINGBACK;
                	mStart = mFinal;
                	int distance = mSpringbackEnd - mFinal;
                	mFinal = mSpringbackEnd;
                	mCoeffDeceleration = 0.95f;
                	mDelta = (float) (distance * (1 - 0.95f) / (1 - Math.pow(0.95, 50)));
                	mVelocity = (int) (mDelta * 50.0f);
                	mCurrVelocity = mVelocity;
                	mLastDistance = 0.0f;
                	mDuration = 1000;
                	mSpringDistance = distance;
                	mStartTime = AnimationUtils.currentAnimationTimeMillis();
            }

            update();
            return true;
        }

        /**
         * Update the current position and velocity for current time. Returns
         * true if update has been done and false if animation duration has been
         * reached.
         * <br/>更新位置和速度。如果滚动已经结束(到时间,经过的时间比mDuration要长?)，返回false,不更新位置和速度，由{@link #continueWhenFinished()}处理
         * <br/>注：这里应该是以滚动的时间来判断是否需要停止的(而不会考虑速度和边界问题)，所以，一开始计算的mDuration很重要。
         */
        private boolean update() {
            final long time = AnimationUtils.currentAnimationTimeMillis();
            final long duration = time - mStartTime;

            // 超时了，fling结束
            if (duration > mDuration) {
            	if(mIterateCount != 0)
            		mFinal = mCurrentPosition;
                mFinished = true;
                return false;
            }
            
            float distance;
            final float t = duration / 1000.0f;
            // 这里同时更新速度和运动距离
            // 正常的fling运动
            if (mState == FLING) {
                
                if(mGravityDeceleration) {
                    mCurrVelocity = mVelocity + mDeceleration * t;
                    distance = mVelocity * t + mDeceleration * t * t / 2.0f;
                }
                else {
                   mUpdateCount++;
                   if(mUpdateCount == MAXUPDATECOUNT) {
                       mAverageTime = (mAverageTime + duration/mUpdateCount)/2;
//                       Log.d(tag, "fling count/update count:"+mFlingTestCount +"/"+mUpdateCount+"/"+mAverageTime);
                       if(mAverageTime < 20) {
                           mDecelerationSpeed = DECELERATIONSPEED_FAST;
                       }
                       else if(mAverageTime < 40) {
                           mDecelerationSpeed =  DECELERATIONSPEED_SLOW;
                       }
                       else if(mAverageTime > 40 && mFlingTestCount >= MAXFLINGTESTCOUNT) {
                           mGravityDeceleration = true;
                       }
                   }
                    
                    mCurrVelocity *= mCoeffDeceleration;
                    distance = mLastDistance + mDelta;
                    mDelta *= mCoeffDeceleration;
                    mLastDistance = distance;
                }
                mCurrentPosition = mStart + (int) distance;
//               Log.d(tag, "current distance＆velocity:"+mCurrentPosition +"/"+mDelta);
            }else if(mState == OVERFLING) {
                mCurrVelocity *= mCoeffDeceleration;
                distance = mLastDistance + mDelta;
                mDelta *= mCoeffDeceleration;
                mLastDistance = distance;
                mCurrentPosition = mStart + (int) distance;
//                Log.d(tag, "current distance＆velocity over fling:"+mCurrentPosition);
            } else {
            	// spring back 或者 spring back for shake
            	mCurrentPosition = exponential(duration,mStart,mSpringDistance,mDuration);
//               Log.d(tag, "current distance＆velocity springback:"+mCurrentPosition);
            }

            if(mCurrentPosition == mFinal)
            	return false;
            else
            	return true;
        }
        
        /**
         * spring back 时，用该方法计算某一时刻需要滚动到的位置
         * @param timePassed 经过了的时间
         * @param start 起始位置
         * @param distance 总路程
         * @param duration 总的时间
         * @return 当前位置
         */
        private int exponential(long timePassed,int start,int distance,long duration) {
//        	return (int) ((t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b);
        	if(mState == SPRINGBACK_FOR_SHAKE){
        		// 这里需要快速一点
        		float coeff = 1.0f * timePassed/duration -1;
            	return (int) (distance * (Math.pow(coeff, 5) + 1) + start);
        	}else{
        		float coeff = 1.0f * timePassed/duration -1;
            	return (int) (distance * (Math.pow(coeff, 5) + 1) + start);
        	}
        }
        
        /**
         * fling时，是否使用重力减速方式。
         * 如果设置为false，则使用移动距离线性减少的方式？
         */
        public void setEnabledGravityDeceleration(boolean enabled){
            mGravityDeceleration = enabled;
        }
    }
    /**
     * 根据当前的overScroll，获取需要移动的距离(对用户鼠标移动的实际距离进行一定比例的缩短)，当overScroll越多，移动距离越小
     * 该方法在onTouchEvent()的ACTION_MOVE时使用
     * @param currentOver	当前点的overScroll距离(已经偏移的位置)
     * @param maxOver		允许最大的overScroll距离
     * @param delta			用户鼠标移动的实际距离
     * @return				这次move需要移动的距离
     */
    public static int getOverMoveDelta(int currentOver, int maxOver, int delta){
    	int incrementalDelta = delta;
    	
    	if(currentOver != 0 && maxOver != 0 && delta != 0){
    		// 这个判断时为何?
        	if((currentOver * delta) > 0){
    		    float coeff = 1 - 1.0f * Math.abs(currentOver) / maxOver;
    		    coeff = coeff < 0 ? 0 : coeff;
                incrementalDelta *= coeff;
                if(delta > 0)
                	incrementalDelta = incrementalDelta == 0 ? 1 :incrementalDelta;
                else
                	incrementalDelta = incrementalDelta == 0 ? -1 :incrementalDelta;
        	}else{
        		incrementalDelta /= 2;
        	}    	
    	}
        return incrementalDelta;
    }
    
    /**
     * 设置{@link #startScroll(int, int, int, int, int)}使用的interpolator
     * @param interpolator 该值如果是null，则使用默认的液体流动计算方式
     */
    public void setInterpolator(Interpolator interpolator){
    	mInterpolator = interpolator;
    }
    
}